﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using WeWork.Common;
using WeWork.Model;

namespace WeWork.Service
{
    /// <summary>
    /// 会话内容存档
    /// 媒体消息的格式分别是什么？
    /// 图片是jpg格式、语音是amr格式、视频/音频是mp4格式、文件格式类型包括在消息体内，表情分为动图(gif)与静态图(png)，在消息体内定义。
    /// </summary>
    public class FinanceHelper
    {
        /// <summary>
        /// 调用企业的企业id，例如：wwd08c8exxxx5ab44d，可以在企业微信管理端--我的企业--企业信息查看
        /// </summary>
        public string CorpId = "";
        /// <summary>
        /// 聊天内容存档的Secret，可以在企业微信管理端--管理工具--聊天内容存档查看
        /// </summary>
        public string Secret = "";
        /// <summary>
        /// 会话加密私钥
        /// </summary>
        public Dictionary<int, string> VerKey = null;
        /// <summary>
        /// 企业微信SDK
        /// </summary>
        public IntPtr SDK = IntPtr.Zero;
        /// <summary>
        /// 每次拉去会话记录数上限为1000（主要是为了防止记录太多爆内存）
        /// </summary>
        public int MaxLimit = 1000;

        /// <summary>
        /// 初始化SDK
        /// </summary>
        /// <param name="corpid">调用企业的企业id，例如：wwd08c8exxxx5ab44d，可以在企业微信管理端--我的企业--企业信息查看</param>
        /// <param name="secret">会话内容存档的Secret，可以在企业微信管理端--管理工具--聊天内容存档查看</param>
        /// <param name="verKey">会话内容存档加密版本的私钥集合， 企业微信的会话内容存档，提供的解密密钥是一段文本字符串，需要将其转换为xml格式。可将文本字符串保存为.pem文件，然后通过以下地址转换： https://the-x.cn/certificate/PemToXml.aspx </param>
        public FinanceHelper(string corpid, string secret, Dictionary<int, string> verKey)
        {
            this.CorpId = corpid;
            this.Secret = secret;
            this.VerKey = verKey;

            IntPtr sdk = Finance.NewSdk();

            //企业微信 corpid，secret
            var result = Finance.Init(sdk, CorpId, Secret);
            if (result == 0)
            {
                this.SDK = sdk;
            }
            else
            {
                Log.Error("FinanceHelper", "企业微信-会话内容存档：初始化SDK阶段返回失败。");
            }
        }

        #region 同步方法
        /// <summary>
        /// 循环获取会话记录数据 以及媒体列表
        /// </summary>
        /// <param name="seq">本次请求获取消息记录开始的seq值。首次访问填写0，非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。Uint64类型，范围0-pow(2,64)-1</param>
        /// <param name="limit">一次调用限制的limit值，不能超过1000.uint32类型</param>
        /// <param name="proxy">使用代理的请求，需要传入代理的链接。如：socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码，需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时长，单位 秒</param>
        /// <returns>消息列表  媒体文件列表 群列表 内部用户列表 外部联系人列表 机器人列表</returns>
        public Tuple<List<IMsgBase>, List<FileData>, List<string>, List<string>, List<string>, List<string>> GetChatList(long seq = 0, long limit = 1000, string proxy = "", string passwd = "", long timeout = 60)
        {
            List<IMsgBase> msgList = new List<IMsgBase>(); //会话列表
            List<FileData> fileList = new List<FileData>(); //媒体文件列表
            List<string> groupList = new List<string>(); //群列表
            List<string> useridList = new List<string>(); //内部用户列表
            List<string> externalList = new List<string>(); //外部联系人列表
            List<string> robotList = new List<string>(); //机器人列表

            DateTime startTime = DateTime.Now;
            int pullNumber = 0; //拉取的次数
            //获取聊天信息
            bool isGet = true;
            while (isGet)
            {
                var result = GetChatData(seq, limit, proxy, passwd, timeout);
                List<IMsgBase> list1 = result.Item1; //企业微信 会话内容 列表
                List<FileData> list2 = result.Item2; //媒体内容 列表
                List<string> list3 = result.Item3; //群列表
                List<string> list4 = result.Item4; //内部用户列表
                List<string> list5 = result.Item5; //外部联系人列表
                List<string> list6 = result.Item6; //机器人列表

                var count = list1.Count;
                if (count > 0)
                {
                    foreach (var item in list1)
                    {
                        if (!msgList.Any(x => x.msgid == item.msgid))
                        {
                            msgList.Add(item);
                        }
                    }
                    foreach (var item in list2)
                    {
                        if(!fileList.Any(x => x.SdkFileId == item.SdkFileId))
                        {
                            fileList.Add(item);
                        }
                    }
                    foreach (var item in list3)
                    {
                        if (!groupList.Contains(item))
                        {
                            groupList.Add(item);
                        }
                    }
                    foreach (var item in list4)
                    {
                        if (!useridList.Contains(item))
                        {
                            useridList.Add(item);
                        }
                    }
                    foreach (var item in list5)
                    {
                        if (!externalList.Contains(item))
                        {
                            externalList.Add(item);
                        }
                    }
                    foreach (var item in list6)
                    {
                        if (!robotList.Contains(item))
                        {
                            robotList.Add(item);
                        }
                    }
                    if (count < MaxLimit)
                    {
                        //总数没有超过 限制拉取的数量时 说明已经拉取完成 不在循环拉取了
                        if (count >= limit)
                        {
                            seq = list1.OrderByDescending(t => t.seq).Select(q => q.seq).FirstOrDefault();
                        }
                        else
                        {
                            isGet = false;
                        }
                    }
                    else
                    {
                        isGet = false;
                    }
                }
                else
                {
                    isGet = false;
                }

                //看看 调用频率不可超过600次 / 分钟。
                pullNumber++;

                DateTime endTime = DateTime.Now;
                TimeSpan timeDifference = endTime - startTime;
                double secondsDifference = timeDifference.TotalSeconds;
                if (secondsDifference < 60)
                {
                    if (pullNumber >= 600)
                    {
                        isGet = false;
                    }
                }
            }
            return Tuple.Create(msgList, fileList, groupList, useridList, externalList, robotList);
        }

        /// <summary>
        /// 获取会话记录数据  以及媒体列表
        /// 注：获取会话记录内容不能超过5天，如果企业需要全量数据，则企业需要定期拉取聊天消息。返回的ChatDatas内容为json格式。
        /// </summary>
        /// <param name="seq">本次请求获取消息记录开始的seq值。首次访问填写0，非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。Uint64类型，范围0-pow(2,64)-1</param>
        /// <param name="limit">一次调用限制的limit值，不能超过1000.uint32类型</param>
        /// <param name="proxy">使用代理的请求，需要传入代理的链接。如：socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码，需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时长，单位 秒</param>
        /// <returns>消息列表  媒体文件列表 群列表 内部用户列表 外部联系人列表 机器人列表</returns>
        public Tuple<List<IMsgBase>, List<FileData>, List<string>, List<string>, List<string>, List<string>> GetChatData(long seq = 0, long limit = 1000, string proxy = "", string passwd = "", long timeout = 300)
        {
            List<IMsgBase> msgList = new List<IMsgBase>(); //会话列表
            List<FileData> fileList = new List<FileData>(); //媒体文件列表
            List<string> groupList = new List<string>(); //群列表
            List<string> useridList = new List<string>(); //内部用户列表
            List<string> externalList = new List<string>(); //外部联系人列表
            List<string> robotList = new List<string>(); //机器人列表
            var slice = Finance.NewSlice();
            try
            {
                var result = Finance.GetChatData(this.SDK, seq, limit, proxy, passwd, timeout, slice);
                if (result == 0)
                {
                    var content = GetContentFromSlice(slice);
                    var encryptChatData = JsonHelper.JsonToT<GetChatData>(content);
                    if (encryptChatData.errcode == 0)
                    {
                        foreach (var encryptChat in encryptChatData.chatdata)
                        {
                            var msgseq = encryptChat.seq;
                            string fileName = "";
                            string md5Sum = "";
                            string sdkFileId = "";
                            IMsgBase msgModel = null;
                            FileData fileData = null;

                            //根据加密此条消息使用的公钥版本号匹配私钥
                            var flag = VerKey.TryGetValue(encryptChat.publickey_ver, out var privateKey);
                            if (flag)
                            {
                                if (!string.IsNullOrWhiteSpace(privateKey))
                                {
                                    var key = Decrypt(privateKey, encryptChat.encrypt_random_key);
                                    if (!string.IsNullOrWhiteSpace(key))
                                    {
                                        var msgSlice = Finance.NewSlice();
                                        var deRes = Finance.DecryptData(key, encryptChat.encrypt_chat_msg, msgSlice);
                                        var chatJsonStr = GetContentFromSlice(msgSlice);

                                        Finance.FreeSlice(msgSlice);

                                        if (!string.IsNullOrWhiteSpace(chatJsonStr))
                                        {
                                            msgModel = JsonHelper.JsonToT<MsgBase>(chatJsonStr);
                                            #region 
                                            var action = msgModel.action;//消息动作，目前有send(发送消息)/recall(撤回消息)/switch(切换企业日志)三种类型。
                                            switch (action)
                                            {
                                                case "switch"://切换企业日志
                                                    msgModel = JsonHelper.JsonToT<ChatSwitch>(chatJsonStr);
                                                    break;
                                                case "recall"://撤回消息
                                                    msgModel = JsonHelper.JsonToT<ChatRevoke>(chatJsonStr);
                                                    break;
                                                case "send"://发送消息
                                                    {
                                                        string roomid = "";
                                                        var sendChat = JsonHelper.JsonToT<MsgChatBase>(chatJsonStr);
                                                        var msgtype = sendChat.msgtype;
                                                        switch (msgtype)
                                                        {
                                                            case MsgType.Text://文本
                                                                ChatText chatText = JsonHelper.JsonToT<ChatText>(chatJsonStr);
                                                                msgModel = chatText;
                                                                roomid = chatText.roomid;
                                                                break;
                                                            case MsgType.Image://图片
                                                                {
                                                                    ChatImage chatImage = JsonHelper.JsonToT<ChatImage>(chatJsonStr);
                                                                    msgModel = chatImage;
                                                                    roomid = chatImage.roomid;
                                                                    fileName = $"{Guid.NewGuid()}.jpg";
                                                                    md5Sum = chatImage.image.md5sum;
                                                                    sdkFileId = chatImage.image.sdkfileid;

                                                                    if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                    {
                                                                        fileData = new FileData()
                                                                        {
                                                                            MsgType = "image",
                                                                            MsgDate = TimeHelper.GetUnixTimeToDateTime(chatImage.msgtime.ToString()),
                                                                            FileName = fileName,
                                                                            Md5Sum = md5Sum,
                                                                            SdkFileId = sdkFileId
                                                                        };
                                                                        fileList.Add(fileData);
                                                                    }
                                                                }
                                                                break;
                                                            case MsgType.Agree://同意会话聊天内容
                                                                ChatAgree chatAgree = JsonHelper.JsonToT<ChatAgree>(chatJsonStr);
                                                                msgModel = chatAgree;
                                                                roomid = chatAgree.roomid;
                                                                break;
                                                            case MsgType.Disagree://不同意会话聊天内容
                                                                ChatDisAgree chatDisAgree = JsonHelper.JsonToT<ChatDisAgree>(chatJsonStr);
                                                                msgModel = JsonHelper.JsonToT<ChatDisAgree>(chatJsonStr);
                                                                roomid = chatDisAgree.roomid;
                                                                break;
                                                            case MsgType.Voice://语音
                                                                {
                                                                    var chatVoice = JsonHelper.JsonToT<ChatVoice>(chatJsonStr);
                                                                    msgModel = chatVoice;
                                                                    roomid = chatVoice.roomid;
                                                                    fileName = $"{Guid.NewGuid()}.amr";
                                                                    md5Sum = chatVoice.voice.md5sum;
                                                                    sdkFileId = chatVoice.voice.sdkfileid;

                                                                    if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                    {
                                                                        fileData = new FileData()
                                                                        {
                                                                            MsgType = "voice",
                                                                            MsgDate = TimeHelper.GetUnixTimeToDateTime(chatVoice.msgtime.ToString()),
                                                                            FileName = fileName,
                                                                            Md5Sum = md5Sum,
                                                                            SdkFileId = sdkFileId
                                                                        };
                                                                        fileList.Add(fileData);
                                                                    }
                                                                }
                                                                break;
                                                            case MsgType.Video://视频
                                                                {
                                                                    var chatVideo = JsonHelper.JsonToT<ChatVideo>(chatJsonStr);
                                                                    msgModel = chatVideo;
                                                                    roomid = chatVideo.roomid;
                                                                    fileName = $"{Guid.NewGuid()}.mp4";
                                                                    md5Sum = chatVideo.video.md5sum;
                                                                    sdkFileId = chatVideo.video.sdkfileid;

                                                                    if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                    {
                                                                        fileData = new FileData()
                                                                        {
                                                                            MsgType = "video",
                                                                            MsgDate = TimeHelper.GetUnixTimeToDateTime(chatVideo.msgtime.ToString()),
                                                                            FileName = fileName,
                                                                            Md5Sum = md5Sum,
                                                                            SdkFileId = sdkFileId
                                                                        };
                                                                        fileList.Add(fileData);
                                                                    }
                                                                }
                                                                break;
                                                            case MsgType.Card://名片
                                                                ChatCard chatCard = JsonHelper.JsonToT<ChatCard>(chatJsonStr);
                                                                msgModel = chatCard;
                                                                roomid = chatCard.roomid;
                                                                break;
                                                            case MsgType.Location://位置
                                                                ChatLocation chatLocation = JsonHelper.JsonToT<ChatLocation>(chatJsonStr);
                                                                msgModel = chatLocation;
                                                                roomid = chatLocation.roomid;
                                                                break;
                                                            case MsgType.Emotion://表情
                                                                {
                                                                    var chatEmotion = JsonHelper.JsonToT<ChatEmotion>(chatJsonStr);
                                                                    msgModel = chatEmotion;
                                                                    roomid = chatEmotion.roomid;
                                                                    switch (chatEmotion.emotion.type)
                                                                    {
                                                                        case 1:
                                                                            fileName = $"{Guid.NewGuid()}.gif";
                                                                            break;
                                                                        case 2:
                                                                            fileName = $"{Guid.NewGuid()}.png";
                                                                            break;
                                                                    }
                                                                    md5Sum = chatEmotion.emotion.md5sum;
                                                                    sdkFileId = chatEmotion.emotion.sdkfileid;

                                                                    if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                    {
                                                                        fileData = new FileData()
                                                                        {
                                                                            MsgType = "image",
                                                                            MsgDate = TimeHelper.GetUnixTimeToDateTime(chatEmotion.msgtime.ToString()),
                                                                            FileName = fileName,
                                                                            Md5Sum = md5Sum,
                                                                            SdkFileId = sdkFileId
                                                                        };
                                                                        fileList.Add(fileData);
                                                                    }
                                                                }
                                                                break;
                                                            case MsgType.File://文件
                                                                {
                                                                    var chatFile = JsonHelper.JsonToT<ChatFile>(chatJsonStr);
                                                                    msgModel = chatFile;
                                                                    roomid = chatFile.roomid;
                                                                    fileName = $"{Guid.NewGuid()}.{chatFile.file.fileext}";
                                                                    md5Sum = chatFile.file.md5sum;
                                                                    sdkFileId = chatFile.file.sdkfileid;

                                                                    if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                    {
                                                                        fileData = new FileData()
                                                                        {
                                                                            MsgType = "file",
                                                                            MsgDate = TimeHelper.GetUnixTimeToDateTime(chatFile.msgtime.ToString()),
                                                                            FileName = fileName,
                                                                            Md5Sum = md5Sum,
                                                                            SdkFileId = sdkFileId
                                                                        };
                                                                        fileList.Add(fileData);
                                                                    }
                                                                }
                                                                break;
                                                            case MsgType.Link://链接
                                                                ChatLink chatLink = JsonHelper.JsonToT<ChatLink>(chatJsonStr);
                                                                msgModel = chatLink;
                                                                roomid = chatLink.roomid;
                                                                break;
                                                            case MsgType.WeApp://小程序消息
                                                                ChatWeapp chatWeapp = JsonHelper.JsonToT<ChatWeapp>(chatJsonStr);
                                                                msgModel = chatWeapp;
                                                                roomid = chatWeapp.roomid;
                                                                break;
                                                            case MsgType.ChatRecord://会话记录消息
                                                                ChatRecord chatRecord = JsonHelper.JsonToT<ChatRecord>(chatJsonStr);
                                                                msgModel = chatRecord;
                                                                roomid = chatRecord.roomid;
                                                                break;
                                                            case MsgType.Todo://待办消息
                                                                ChatTodo chatTodo = JsonHelper.JsonToT<ChatTodo>(chatJsonStr);
                                                                msgModel = chatTodo;
                                                                roomid = chatTodo.roomid;
                                                                break;
                                                            case MsgType.Vote://投票消息
                                                                ChatVote chatVote = JsonHelper.JsonToT<ChatVote>(chatJsonStr);
                                                                msgModel = chatVote;
                                                                roomid = chatVote.roomid;
                                                                break;
                                                            case MsgType.Collect://填表消息
                                                                ChatCollect chatCollect = JsonHelper.JsonToT<ChatCollect>(chatJsonStr);
                                                                msgModel = chatCollect;
                                                                roomid = chatCollect.roomid;
                                                                break;
                                                            case MsgType.RedPacket://红包消息
                                                                ChatRedPacket chatRedPacket = JsonHelper.JsonToT<ChatRedPacket>(chatJsonStr);
                                                                msgModel = chatRedPacket;
                                                                roomid = chatRedPacket.roomid;
                                                                break;
                                                            case MsgType.Meeting://会议邀请消息
                                                                ChatMeeting chatMeeting = JsonHelper.JsonToT<ChatMeeting>(chatJsonStr);
                                                                msgModel = chatMeeting;
                                                                roomid = chatMeeting.roomid;
                                                                break;
                                                            case MsgType.DocMsg://在线文档消息
                                                                ChatDocMsg chatDocMsg = JsonHelper.JsonToT<ChatDocMsg>(chatJsonStr);
                                                                msgModel = chatDocMsg;
                                                                roomid = chatDocMsg.roomid;
                                                                break;
                                                            case MsgType.Markdown://MarkDown格式消息
                                                                ChatMarkDown chatMarkDown = JsonHelper.JsonToT<ChatMarkDown>(chatJsonStr);
                                                                msgModel = JsonHelper.JsonToT<ChatMarkDown>(chatJsonStr);
                                                                roomid = chatMarkDown.roomid;
                                                                break;
                                                            case MsgType.News://图文消息
                                                                ChatNews chatNews = JsonHelper.JsonToT<ChatNews>(chatJsonStr);
                                                                msgModel = chatNews;
                                                                roomid = chatNews.roomid;
                                                                break;
                                                            case MsgType.Calendar://日程消息
                                                                ChatCalendar chatCalendar = JsonHelper.JsonToT<ChatCalendar>(chatJsonStr);
                                                                msgModel = chatCalendar;
                                                                roomid = chatCalendar.roomid;
                                                                break;
                                                            case MsgType.Mixed://混合消息
                                                                ChatMixed chatMixed = JsonHelper.JsonToT<ChatMixed>(chatJsonStr);
                                                                msgModel = chatMixed;
                                                                roomid = chatMixed.roomid;
                                                                break;
                                                            case MsgType.MeetingVoiceCall://音频存档消息
                                                                {
                                                                    var chatMeetingVoiceCall = JsonHelper.JsonToT<ChatMeetingVoiceCall>(chatJsonStr);
                                                                    msgModel = chatMeetingVoiceCall;
                                                                    fileName = $"{Guid.NewGuid()}.mp4";
                                                                    md5Sum = "";
                                                                    sdkFileId = chatMeetingVoiceCall.meeting_voice_call.sdkfileid;

                                                                    if (!fileList.Any(t => t.SdkFileId == sdkFileId))
                                                                    {
                                                                        fileData = new FileData()
                                                                        {
                                                                            MsgType = "video",
                                                                            MsgDate = TimeHelper.GetUnixTimeToDateTime(chatMeetingVoiceCall.msgtime.ToString()),
                                                                            FileName = fileName,
                                                                            Md5Sum = md5Sum,
                                                                            SdkFileId = sdkFileId
                                                                        };
                                                                        fileList.Add(fileData);
                                                                    }
                                                                }
                                                                break;
                                                            case MsgType.VoipDocShare://音频共享文档消息
                                                                {
                                                                    var chatVoipDocShare = JsonHelper.JsonToT<ChatVoipDocShare>(chatJsonStr);
                                                                    msgModel = chatVoipDocShare;
                                                                    fileName = $"{chatVoipDocShare.voip_doc_share.filename}";
                                                                    md5Sum = chatVoipDocShare.voip_doc_share.md5sum; ;
                                                                    sdkFileId = chatVoipDocShare.voip_doc_share.sdkfileid;

                                                                    if (!fileList.Any(t => t.SdkFileId == chatVoipDocShare.voip_doc_share.sdkfileid))
                                                                    {
                                                                        fileData = new FileData()
                                                                        {
                                                                            MsgType = "file",
                                                                            MsgDate = TimeHelper.GetUnixTimeToDateTime(chatVoipDocShare.msgtime.ToString()),
                                                                            FileName = fileName,
                                                                            Md5Sum = md5Sum,
                                                                            SdkFileId = sdkFileId
                                                                        };
                                                                        fileList.Add(fileData);
                                                                    }
                                                                }
                                                                break;
                                                            case MsgType.ExternalRedPacket://互通红包消息
                                                                ChatExternalRedPacket chatExternalRedPacket = JsonHelper.JsonToT<ChatExternalRedPacket>(chatJsonStr);
                                                                msgModel = chatExternalRedPacket;
                                                                roomid = chatExternalRedPacket.roomid;
                                                                break;
                                                            case MsgType.SphFeed://视频号消息
                                                                ChatSphFeed chatSphFeed = JsonHelper.JsonToT<ChatSphFeed>(chatJsonStr);
                                                                msgModel = chatSphFeed;
                                                                roomid = chatSphFeed.roomid;
                                                                break;
                                                            case MsgType.VoipText://音视频通话
                                                                ChatVoipText chatVoipText = JsonHelper.JsonToT<ChatVoipText>(chatJsonStr);
                                                                msgModel = chatVoipText;
                                                                roomid = chatVoipText.roomid;
                                                                break;
                                                            case MsgType.QyDiskFile://微盘文件 
                                                                ChatQyDiskFile chatQyDiskFile = JsonHelper.JsonToT<ChatQyDiskFile>(chatJsonStr);
                                                                msgModel = chatQyDiskFile;
                                                                roomid = chatQyDiskFile.roomid;
                                                                break;
                                                        }

                                                        //获取群列表
                                                        if (!string.IsNullOrWhiteSpace(roomid))
                                                        {
                                                            if (!groupList.Contains(roomid))
                                                            {
                                                                groupList.Add(roomid);
                                                            }
                                                        }

                                                        /*
                                                        机器人与外部联系人的账号都是external_userid，其中机器人的external_userid是以"wb"开头，例如："wbjc7bDwAAJVylUKpSA3Z5U11tDO4AAA"，外部联系人的external_userid以"wo"或"wm"开头。
                                                        如果是机器人发出的消息，可以通过openapi拉取机器人详情：如何获取机器人详情？
                                                        如果是外部联系人发出的消息，可以通过openapi拉取外部联系人详情：如何获取外部联系人详情？ 
                                                        */
                                                        var from = sendChat.from;
                                                        if(!string.IsNullOrWhiteSpace(from))
                                                        {
                                                            if (from.StartsWith("wb")) //机器人
                                                            {
                                                                if (!robotList.Contains(from))
                                                                {
                                                                    robotList.Add(from);
                                                                }
                                                            }
                                                            else if (from.StartsWith("wo") || from.StartsWith("wm")) //外部联系人
                                                            {
                                                                if (!externalList.Contains(from))
                                                                {
                                                                    externalList.Add(from);
                                                                }
                                                            }
                                                            else  //企业内部人员
                                                            {
                                                                if (!useridList.Contains(from))
                                                                {
                                                                    useridList.Add(from);
                                                                }
                                                            }
                                                        }

                                                        var tolist = sendChat.tolist;
                                                        if(tolist!=null)
                                                        {
                                                            foreach (var user in tolist)
                                                            {
                                                                if (user.StartsWith("wb")) //机器人
                                                                {
                                                                    if (!robotList.Contains(user))
                                                                    {
                                                                        robotList.Add(user);
                                                                    }
                                                                }
                                                                else if (user.StartsWith("wo") || user.StartsWith("wm")) //外部联系人
                                                                {
                                                                    if (!externalList.Contains(user))
                                                                    {
                                                                        externalList.Add(user);
                                                                    }
                                                                }
                                                                else  //企业内部人员
                                                                {
                                                                    if (!useridList.Contains(user))
                                                                    {
                                                                        useridList.Add(user);
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                    break;
                                                default:
                                                    break;
                                            }

                                            /*
                                            图片
                                            {
                                                "msgid":"CAQQvPnc4QUY0On2rYSAgAMgooLa0Q8=","action":"send","from":"XuJinSheng","tolist":["icefog"],"roomid":"",
                                                "msgtime":0,"msgtype":"image",
                                                "image":{"md5sum":"50de8e5ae8ffe4f1df7a93841f71993a","filesize":70961,"sdkfileid":"CtYBMzA2OTAyMDEwMjA0NjIzMDYwMDIwMTAwMDIwNGI3ZmU0MDZlMDIwMzBmNTliMTAyMDQ1YzliNTQ3NzAyMDQ1YzM3M2NiYzA0MjQ2NjM0MzgzNTM0NjEzNTY1MmQzNDYxMzQzODJkMzQzMTYxNjEyZDM5NjEzOTM2MmQ2MTM2NjQ2NDY0NjUzMDY2NjE2NjM1MzcwMjAxMDAwMjAzMDExNTQwMDQxMDUwZGU4ZTVhZThmZmU0ZjFkZjdhOTM4NDFmNzE5OTNhMDIwMTAyMDIwMTAwMDQwMBI4TkRkZk1UWTRPRGcxTVRBek1ETXlORFF6TWw4eE9UUTVOamN6TkRZMlh6RTFORGN4TWpNNU1ERT0aIGEwNGQwYWUyM2JlYzQ3NzQ5MjZhNWZjMjk0ZTEyNTkz"}
                                            }
                                            语音
                                            {
                                                "msgid":"10958372969718811103_1603875609","action":"send","from":"wmGAgeDQAAdBjb8CK4ieMPRm7Cqm-9VA","tolist":["kenshin"],"roomid":"",
                                                "msgtime":1603875609704,"msgtype":"voice",
                                                "voice":{"md5sum":"9db09c7fa627c9e53f17736c786a74d5","voice_size":6810,"play_length":10,"sdkfileid":"kcyZjZqOXhETGYxajB2Zkp5Rk8zYzh4RVF3ZzZGdXlXNWRjMUoxVGZxbzFTTDJnQ2YxL0NraVcxUUJNK3VUamhEVGxtNklCbjZmMEEwSGRwN0h2cU1GQTU1MDRSMWdTSmN3b25ZMkFOeG5hMS90Y3hTQ0VXRlVxYkR0Ymt5c3JmV2VVcGt6UlNXR1ZuTFRWVGtudXVldDRjQ3hscDBrMmNhMFFXVnAwT3Y5NGVqVGpOcWNQV2wrbUJwV01TRm9xWmNDRVVrcFY5Nk9OUS9GbXIvSmZvOVVZZjYxUXBkWnMvUENkVFQxTHc2N0drb2pJT0FLZnhVekRKZ1FSNDU3ZnZtdmYvTzZDOG9DRXl2SUNIOHc9PRI0TkRkZk56ZzRNVE13TVRjMk5qQTRNak0yTmw4ek5qRTVOalExTjE4eE5qQXpPRGMxTmpBNRogNzM3MDY2NmM2YTc5Njg3NDdhNzU3NDY0NzY3NTY4NjY="}
                                            }
                                            视频
                                            {
                                                "msgid":"17955920891003447432_1603875627","action":"send","from":"kenshin","tolist":["wmGAgeDQAAHuRJbt4ZQI_1cqoQcf41WQ"],"roomid":"",
                                                "msgtime":1603875626823,"msgtype":"video",
                                                "video":{"md5sum":"d06fc80c01d6fbffcca3b229ba41eac6","filesize":15169724,"play_length":108,"sdkfileid":"MzAzMjYxMzAzNTYzMzgzMjMyMzQwMjAxMDAwMjA0MDBlNzc4YzAwNDEwZDA2ZmM4MGMwMWQ2ZmJmZmNjYTNiMjI5YmE0MWVhYzYwMjAxMDQwMjAxMDAwNDAwEjhORGRmTVRZNE9EZzFNREEyTlRjM056QXpORjgxTWpZeE9USTBOek5mTVRZd016ZzNOVFl5Tnc9PRogNTIzNGQ1NTQ5N2RhNDM1ZDhlZTU5ODk4NDQ4NzRhNDk="}
                                            }
                                            表情
                                            {
                                                "msgid":"6623217619416669654_1603875612","action":"send","from":"icef","tolist":["wmErxtDgAAhteCglUZH2kUt3rq431qmg"],"roomid":"",
                                                "msgtime":1603875611148,"msgtype":"emotion",
                                                "emotion":{"type":1,"width":290,"height":290,"imagesize":962604,"md5sum":"94c2b0bba52cc456cb8221b248096612","sdkfileid":"4eE1ESTVNalE1TnprMFh6RTJNRE00TnpVMk1UST0aIDc0NzI2NjY1NzE3NTc0Nzg2ZDZlNzg2YTY5NjY2MTYx"}
                                            }
                                            文件
                                            {
                                                "msgid":"18039699423706571225_1603875608","action":"send","from":"kens","tolist":["wmErxtDgAArDlFIhf76O6w4GxU81al8w"],"roomid":"",
                                                "msgtime":1603875608214,"msgtype":"file",
                                                "file":{"md5sum":"18e93fc2ea884df23b3d2d3b8667b9f0","filename":"资料.docx","fileext":"docx","filesize":18181,"sdkfileid":"E4ODRkZjIzYjNkMmQzYjg2NjdiOWYwMDIwMTA1MDIwMTAwMDQwMBI4TkRkZk1UWTRPRGcxTURrek9UZzBPVEF6TTE4eE1EUXpOVGcxTlRVNVh6RTJNRE00TnpVMk1EZz0aIDMwMzkzMzY0NjEzNjM3NjY2NDY1NjMzNjYxMzIzNzYx"}
                                            }
                                            会话记录消息
                                            ype	每条聊天记录的具体消息类型：ChatRecordText/ ChatRecordFile/ ChatRecordImage/ ChatRecordVideo/ ChatRecordLink/ ChatRecordLocation/ ChatRecordMixed ….
                                            {
                                                "msgid":"11354299838102555191_1603875658","action":"send","from":"ken","tolist":["icef"],"roomid":"",
                                                "msgtime":1603875657905,"msgtype":"chatrecord",
                                                "chatrecord":{
                                                    "title":"群聊",
                                                    "item":[
                                                        {"type":"ChatRecordText","msgtime":1603875610,"content":"{\"content\":\"test\"}","from_chatroom":false},
                                                        {"type":"ChatRecordText","msgtime":1603875620,"content":"{\"content\":\"test2\"}","from_chatroom":false}
                                                    ]
                                                }
                                            }

                                            混合消息
                                            mixed	消息内容。可包含图片、文字、表情等多种消息。Object类型
                                            {
                                                "msgid":"DAQQluDa4QUY0On4kYSABAMgzPrShAE=","action":"send","from":"HeMiao","tolist":["HeChangTian","LiuZeYu"],"roomid":"wr_tZ2BwAAUwHpYMwy9cIWqnlU3Hzqfg",
                                                "msgtime":1577414359072,"msgtype":"mixed",
                                                "mixed":{
                                                    "item":[
                                                        {"type":"text","content":"{\"content\":\"你好[微笑]\\n\"}"},
                                                        {"type":"image","content":"{\"md5sum\":\"368b6c18c82e6441bfd89b343e9d2429\",\"filesize\":13177,\"sdkfileid\":\"CtYBMzA2OTAyMDEwMjA0NjIzMDYwMDIwMTAwMDWwNDVmYWY4Y2Q3MDIwMzBmNTliMTAyMDQwYzljNTQ3NzAyMDQ1ZTA1NmFlMjA0MjQ2NjM0NjIzNjY2MzYzNTMyMmQzNzYxMzQ2NDJkMzQ2MjYxNjQyZDM4MzMzMzM4MmQ3MTYyMzczMTM4NjM2NDYxMzczMjY2MzkwMjAxMDAwMjAzMDIwMDEwMDQxMDM2OGI2YzE4YzgyZTY0NDFiZmQ4OWIyNDNlOWQyNDI4MDIwMTAyMDIwMTAwMDQwMBI4TkRkZk2UWTRPRGcxTVRneE5URTFNRGc1TVY4eE1UTTFOak0yTURVeFh6RTFOemMwTVRNek5EYz0aIDQzMTY5NDFlM2MxZDRmZjhhMjEwY2M0NDQzZGUXOTEy\"}"}
                                                    ]
                                                }
                                            }
                                            音频存档消息
                                            {
                                                "msgid":"17952229780246929345_1594197637","action":"send","from":"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA","tolist":["wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA"],
                                                "msgtime":1594197581203,"msgtype":"meeting_voice_call","voiceid":"grb8a4c48a3c094a70982c518d55e40557",
                                                "meeting_voice_call":{
                                                    "endtime":1594197635,"sdkfileid":"CpsBKjAqd0xhb2JWRUJldGtwcE5DVTB6UjRUalN6c09vTjVyRnF4YVJ5M24rZC9YcHF3cHRPVzRwUUlaMy9iTytFcnc0SlBkZDU1YjRNb0MzbTZtRnViOXV5WjUwZUIwKzhjbU9uRUlxZ3pyK2VXSVhUWVN2ejAyWFJaTldGSkRJVFl0aUhkcVdjbDJ1L2RPbjJsRlBOamJaVDNnPT0SOE5EZGZNVFk0T0RnMU16YzVNVGt5T1RJMk9GOHhNalk0TXpBeE9EZzJYekUxT1RReE9UYzJNemM9GiA3YTYyNzA3NTY4Nzc2MTY3NzQ2MTY0NzA2ZTc4NjQ2OQ==",
                                                    "demofiledata":[{"filename":"65eb1cdd3e7a3c1740ecd74220b6c627.docx","demooperator":"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA","starttime":1594197599,"endtime":1594197609}],
                                                    "sharescreendata":[{"share":"wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA","starttime":1594197624,"endtime":1594197624}]
                                                }
                                            } 
                                            音频共享文档消息
                                            {
                                                "msgid":"16527954622422422847_1594199256","action":"send","from":"18002520162","tolist":["wo137MCgAAYW6pIiKKrDe5SlzEhSgwbA"],
                                                "msgtime":1594199235014,"msgtype":"voip_doc_share","voipid":"gr2751c98b19300571f8afb3b74514bd32",
                                                "voip_doc_share":{
                                                    "filename":"欢迎使用微盘.pdf.pdf","md5sum":"ff893900f24e55e216e617a40e5c4648","filesize":4400654,"sdkfileid":"CpsBKjAqZUlLdWJMd2gvQ1JxMzd0ZjlpdW5mZzJOOE9JZm5kbndvRmRqdnBETjY0QlcvdGtHSFFTYm95dHM2VlllQXhkUUN5KzRmSy9KT3pudnA2aHhYZFlPemc2aVZ6YktzaVh3YkFPZHlqNnl2L2MvcGlqcVRjRTlhZEZsOGlGdHJpQ2RWSVNVUngrVFpuUmo3TGlPQ1BJemlRPT0SOE5EZGZNVFk0T0RnMU16YzVNVGt5T1RJMk9GODFNelUyTlRBd01qQmZNVFU1TkRFNU9USTFOZz09GiA3YTcwNmQ2Zjc5NjY3MDZjNjY2Zjc4NzI3NTZmN2E2YQ=="
                                                }
                                            }
                                            */
                                            #endregion
                                            if (msgModel != null)
                                            {
                                                msgModel.seq = msgseq;
                                                msgList.Add(msgModel);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        Log.Error("FinanceHelper", "企业微信-会话内容存档：会话数据解密失败。");
                                    }
                                }
                                else
                                {
                                    Log.Error("FinanceHelper", "企业微信-会话内容存档：会话数据解密私钥为空。");
                                }
                            }
                        }
                    }
                    else
                    {
                        Log.Error("FinanceHelper", $"企业微信-会话内容存档：转换会话数据阶段返回值错误\n[{encryptChatData.errcode}]{encryptChatData.errmsg}");
                    }
                }
                else
                {
                    Log.Error("FinanceHelper", "企业微信-会话内容存档：获取会话数据阶段返回失败。");
                }
            }
            catch (Exception ex)
            {
                Log.Error("FinanceHelper", "企业微信-会话内容存档：获取会话数据数据异常。" + ex.ToString());
            }
            finally
            {
                Finance.FreeSlice(slice);
            }
            return Tuple.Create(msgList, fileList, groupList, useridList, externalList, robotList);
        }

        /// <summary>
        /// 读取媒体数据
        /// 也只能拉取5天内的媒体消息内容
        /// </summary>
        /// <param name="sdkfileid">消息体内容中的sdkfileid信息。</param>
        /// <param name="proxy">使用代理的请求，需要传入代理的链接。如：socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码，需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时间，单位秒</param>
        /// <returns>byte[]</returns>
        public byte[] GetMediaData(string sdkfileid, string proxy = "", string passwd = "", long timeout = 300)
        {
            /*
            错误码：
            返回值	说明	建议
            10000	请求参数错误	检查Init接口corpid、secret参数；检查GetChatData接口limit参数是否未填或大于1000；检查GetMediaData接口sdkfileid是否为空，indexbuf是否正常
            10001	网络请求错误	检查是否网络有异常、波动；检查使用代理的情况下代理参数是否设置正确的用户名与密码
            10002	数据解析失败	建议重试请求。若仍失败，可以反馈给企业微信进行查询，请提供sdk接口参数与调用时间点等信息
            10003	系统调用失败	GetMediaData调用失败，建议重试请求。若仍失败，可以反馈给企业微信进行查询，请提供sdk接口参数与调用时间点等信息
            10004	已废弃	        目前不会返回此错误码
            10005	fileid错误	    检查在GetMediaData接口传入的sdkfileid是否正确
            10006	解密失败	    请检查是否先进行base64decode再进行rsa私钥解密，再进行DecryptMsg调用
            10007	已废弃	        目前不会返回此错误码
            10008	DecryptMsg错误	建议重试请求。若仍失败，可以反馈给企业微信进行查询，请提供sdk接口参数与调用时间点等信息
            10009	ip非法	        请检查sdk访问外网的ip是否与管理端设置的可信ip匹配，若不匹配会返回此错误码
            10010	请求的数据过期	用户欲拉取的数据已过期，仅支持近5天内的数据拉取
            10011	ssl证书错误	    使用openssl版本sdk，校验ssl证书失败 
            */
            var byteList = new List<byte>();
            try
            {
                var outIndexBuf = "";//媒体消息分片拉取，需要填入每次拉取的索引信息。首次不需要填写，后续每次调用只需要将上次调用返回的outindexbuf填入即可。

                while (true)
                {
                    //    int retryCount = 0;

                    //RetryGetMedia:
                    var mediaData = Finance.NewMediaData();
                    var res = Finance.GetMediaData(this.SDK, outIndexBuf, sdkfileid, proxy, passwd, timeout, mediaData);
                    if (res == 0)
                    {
                        var dataIntPtr = Finance.GetData(mediaData);
                        var dataLen = Finance.GetDataLen(mediaData);
                        var bytes = new byte[dataLen];
                        Marshal.Copy(dataIntPtr, bytes, 0, bytes.Length);
                        byteList.AddRange(bytes);

                        // 校验文件是否已经读取完毕
                        if (Finance.IsMediaDataFinish(mediaData) == 1)
                        {
                            Finance.FreeMediaData(mediaData);
                            break;
                        }
                        else
                        {
                            var oibPtr = Finance.GetOutIndexBuf(mediaData);
                            outIndexBuf = Marshal.PtrToStringAnsi(oibPtr);
                            Finance.FreeMediaData(mediaData);
                        }
                    }
                    else
                    {
                        byteList = null;

                        //10001 网络请求错误
                        //10002 解析数据失败
                        //10003 系统调用失败
                        //if (res == 10002 && retryCount < 3)
                        //{
                        //    retryCount++;
                        //    System.Threading.Thread.Sleep(500);
                        //    goto RetryGetMedia;
                        //}
                        //else
                        //{
                        Finance.FreeMediaData(mediaData);
                        //throw new Exception($"企微会话存档:获取会话媒体数据失败，res:{res}");
                        Log.Error("FinanceHelper", $"企业微信-会话内容存档：获取会话媒体数据失败，sdkfileid:{sdkfileid};res:{res}");
                        break;
                        //}
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("FinanceHelper", "企业微信-会话内容存档：获取会话媒体数据失败！" + ex.ToString());
            }
            if (byteList != null && byteList.Count > 0)
            {
                return byteList.ToArray();
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 获取媒体数据列表（含文件byte[]）
        /// </summary>
        /// <param name="list">媒体数据列表</param>
        /// <param name="proxy">使用代理的请求，需要传入代理的链接。如：socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码，需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时间，单位秒</param>
        /// <returns></returns>
        public List<FileData> GetFileList(List<FileData> list, string proxy = "", string passwd = "", long timeout = 300)
        {
            List<FileData> listNew = null;
            if (list.Count > 0)
            {
                listNew = new List<FileData>();
                foreach (var file in list)
                {
                    byte[] buffer = null;
                    //也只能拉取5天内的媒体消息内容
                    if (file.MsgDate > DateTime.Now.AddDays(-5))
                    {
                        buffer = GetMediaData(file.SdkFileId, proxy, passwd, timeout);
                    }
                    file.FileByte = buffer;
                    listNew.Add(file);
                }
            }
            return listNew;
        }

        #endregion 同步方法

        #region 异步方法

        /// <summary>
        /// 获取会话记录数据（异步方法）
        /// 注：获取会话记录内容不能超过5天，如果企业需要全量数据，则企业需要定期拉取聊天消息。返回的ChatDatas内容为json格式。
        /// </summary>
        /// <param name="seq">本次请求获取消息记录开始的seq值。首次访问填写0，非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。Uint64类型，范围0-pow(2,64)-1</param>
        /// <param name="limit">一次调用限制的limit值，不能超过1000.uint32类型</param>
        /// <param name="proxy">使用代理的请求，需要传入代理的链接。如：socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码，需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时长，单位 秒</param>
        /// <returns>byte[]</returns>
        public async Task<Tuple<List<IMsgBase>, List<FileData>, List<string>, List<string>, List<string>, List<string>>> GetChatDataAsync(long seq = 0, long limit = 1000, string proxy = "", string passwd = "", long timeout = 300)
        {
            // 调用同步方法并转换为异步
            var list = GetChatData(seq, limit, proxy, passwd, timeout);
            return await Task.FromResult(list);
        }

        /// <summary>
        /// 循环获取会话记录数据
        /// </summary>
        /// <param name="seq">本次请求获取消息记录开始的seq值。首次访问填写0，非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。Uint64类型，范围0-pow(2,64)-1</param>
        /// <param name="limit">一次调用限制的limit值，不能超过1000.uint32类型</param>
        /// <param name="proxy">使用代理的请求，需要传入代理的链接。如：socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码，需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时长，单位 秒</param>
        /// <returns></returns>
        public async Task<Tuple<List<IMsgBase>, List<FileData>, List<string>, List<string>, List<string>, List<string>>> GetChatListAsync(long seq = 0, long limit = 1000, string proxy = "", string passwd = "", long timeout = 60)
        {
            List<IMsgBase> msgList = new List<IMsgBase>();
            List<FileData> fileList = new List<FileData>();
            List<string> groupList = new List<string>(); //群列表
            List<string> useridList = new List<string>(); //内部用户列表
            List<string> externalList = new List<string>(); //外部联系人列表
            List<string> robotList = new List<string>(); //机器人列表
            DateTime startTime = DateTime.Now;
            int pullNumber = 0; //拉取的次数
            //获取聊天信息
            bool isGet = true;
            while (isGet)
            {
                var result = await GetChatDataAsync(seq, limit, proxy, passwd, timeout);
                List<IMsgBase> list1 = result.Item1; //企业微信 会话内容 列表
                List<FileData> list2 = result.Item2; //媒体内容 列表
                List<string> list3 = result.Item3; //群列表
                List<string> list4 = result.Item4; //内部用户列表
                List<string> list5 = result.Item5; //外部联系人列表
                List<string> list6 = result.Item6; //机器人列表

                var count = list1.Count;
                if (count > 0)
                {
                    foreach (var item in list1)
                    {
                        if (!msgList.Any(x => x.msgid == item.msgid))
                        {
                            msgList.Add(item);
                        }
                    }
                    foreach (var item in list2)
                    {
                        if (!fileList.Any(x => x.SdkFileId == item.SdkFileId))
                        {
                            fileList.Add(item);
                        }
                    }
                    foreach (var item in list3)
                    {
                        if (!groupList.Contains(item))
                        {
                            groupList.Add(item);
                        }
                    }
                    foreach (var item in list4)
                    {
                        if (!useridList.Contains(item))
                        {
                            useridList.Add(item);
                        }
                    }
                    foreach (var item in list5)
                    {
                        if (!externalList.Contains(item))
                        {
                            externalList.Add(item);
                        }
                    }
                    foreach (var item in list6)
                    {
                        if (!robotList.Contains(item))
                        {
                            robotList.Add(item);
                        }
                    }

                    if (count < MaxLimit)
                    {
                        //总数没有超过 限制拉取的数量时 说明已经拉取完成 不在循环拉取了
                        if (count >= limit)
                        {
                            seq = list1.OrderByDescending(t => t.seq).Select(q => q.seq).FirstOrDefault();
                        }
                        else
                        {
                        isGet = false;
                        }
                    }
                    else
                    {
                        isGet = false;
                    }
                }
                else
                {
                    isGet = false;
                }

                //看看 调用频率不可超过600次 / 分钟。
                pullNumber++;

                DateTime endTime = DateTime.Now;
                TimeSpan timeDifference = endTime - startTime;
                double secondsDifference = timeDifference.TotalSeconds;
                if (secondsDifference < 60)
                {
                    if (pullNumber >= 600)
                    {
                        isGet = false;
                    }
                }
            }
            return Tuple.Create(msgList, fileList, groupList, useridList, externalList, robotList);
        }

        /// <summary>
        /// 读取媒体数据（异步方法）
        /// 也只能拉取5天内的媒体消息内容
        /// </summary>
        /// <param name="sdkfileid">消息体内容中的sdkfileid信息。</param>
        /// <param name="proxy">使用代理的请求，需要传入代理的链接。如：socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码，需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时间，单位秒</param>
        /// <returns>byte[]</returns>
        public async Task<byte[]> GetMediaDataAsync(string sdkfileid, string proxy = "", string passwd = "", long timeout = 300)
        {
            // 调用同步方法并转换为异步
            var bytes = GetMediaData(sdkfileid, proxy, passwd, timeout);
            return await Task.FromResult(bytes);
        }

        /// <summary>
        /// 获取媒体数据列表（含文件byte[]）
        /// </summary>
        /// <param name="list">媒体数据列表</param>
        /// <param name="proxy">使用代理的请求，需要传入代理的链接。如：socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081.如不使用代理可以设置为空. 支持sock5跟http代理</param>
        /// <param name="passwd">代理账号密码，需要传入代理的账号密码。如 user_name:passwd_123</param>
        /// <param name="timeout">超时时间，单位秒</param>
        /// <returns></returns>
        public async Task<List<FileData>> GetFileListAync(List<FileData> list, string proxy = "", string passwd = "", long timeout = 300)
        {
            List<FileData> listNew = null;
            if (list.Count > 0)
            {
                listNew = new List<FileData>();
                foreach (var file in list)
                {
                    byte[] buffer = null;
                    //也只能拉取5天内的媒体消息内容
                    if (file.MsgDate > DateTime.Now.AddDays(-5))
                    {
                        buffer = await GetMediaDataAsync(file.SdkFileId, proxy, passwd, timeout);
                    }
                    file.FileByte = buffer;
                    listNew.Add(file);
                }
            }
            return listNew;
        }

        #endregion 异步方法

        /// <summary>
        /// 获取文本
        /// </summary>
        /// <param name="slice"></param>
        /// <returns></returns>
        public string GetContentFromSlice(IntPtr slice)
        {
            var length = Finance.GetSliceLen(slice);
            var bytes = new byte[length];
            var ptr = Finance.GetContentFromSlice(slice);
            Marshal.Copy(ptr, bytes, 0, bytes.Length);
            return Encoding.UTF8.GetString(bytes);
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="privateKey">私钥 “-----BEGIN RSA PRIVATE KEY-----”开头 “-----END RSA PRIVATE KEY-----”结尾</param>
        /// <param name="text">使用publickey_ver指定版本的公钥进行非对称加密后base64加密的内容</param>
        /// <returns></returns>
        public string Decrypt(string privateKey, string text)
        {
            var rsa = new RSACryptoServiceProvider();
            var bytes = Convert.FromBase64String(text);
            rsa.FromXmlString(RSAKeyConvert.PrivateKeyPemToXml(privateKey));
            var result = rsa.Decrypt(bytes, false);
            return Encoding.UTF8.GetString(result);
        }
    }
}
